package br.ufpe.cin.ip.biblioteca.negocio;

import br.ufpe.cin.ip.biblioteca.entidades.Funcionario;
import br.ufpe.cin.ip.biblioteca.entidades.Publicacao;
import br.ufpe.cin.ip.biblioteca.entidades.Usuario;
import br.ufpe.cin.ip.biblioteca.excecoes.ArquivoConfiguracaoNaoEncontradoException;
import br.ufpe.cin.ip.biblioteca.excecoes.FuncionarioJaCadastradoException;
import br.ufpe.cin.ip.biblioteca.excecoes.FuncionarioNaoEncontradoException;
import br.ufpe.cin.ip.biblioteca.excecoes.LoginSenhaIncorretosException;
import br.ufpe.cin.ip.biblioteca.excecoes.PublicacaoJaCadastradaException;
import br.ufpe.cin.ip.biblioteca.excecoes.PublicacaoJaEmprestadaException;
import br.ufpe.cin.ip.biblioteca.excecoes.PublicacaoNaoEmprestadaException;
import br.ufpe.cin.ip.biblioteca.excecoes.PublicacaoNaoEncontradaException;
import br.ufpe.cin.ip.biblioteca.excecoes.RepositorioException;
import br.ufpe.cin.ip.biblioteca.excecoes.UsuarioJaCadastradoException;
import br.ufpe.cin.ip.biblioteca.excecoes.UsuarioNaoEncontradoException;
import br.ufpe.cin.ip.biblioteca.iterator.IteratorFuncionario;
import br.ufpe.cin.ip.biblioteca.iterator.IteratorPublicacao;
import br.ufpe.cin.ip.biblioteca.iterator.IteratorUsuario;
import br.ufpe.cin.ip.biblioteca.relatorios.GeradorRelatorios;
import br.ufpe.cin.ip.biblioteca.relatorios.Relatorio;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioFuncionario;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioFuncionarioArquivo;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioFuncionarioArray;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioFuncionarioLista;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioPublicacao;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioPublicacaoArquivo;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioPublicacaoArray;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioPublicacaoLista;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioUsuario;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioUsuarioArquivo;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioUsuarioArray;
import br.ufpe.cin.ip.biblioteca.repositorios.RepositorioUsuarioLista;
import br.ufpe.cin.ip.biblioteca.util.ArquivoUtil;

public class Fachada {
	private static Fachada instancia;
	private CadastroFuncionario cadastroFuncionario;
	private CadastroPublicacao cadastroPublicacao;
	private CadastroUsuario cadastroUsuario;
	private GeradorRelatorios geradorRelatorios;
	
	private Fachada() throws ArquivoConfiguracaoNaoEncontradoException {
		try {
			String tipoRepositorios = ArquivoUtil.carregarTipoRepositorios();
			
			RepositorioFuncionario repositorioFuncionario = null;
			RepositorioPublicacao repositorioPublicacao = null;
			RepositorioUsuario repositorioUsuario = null;
			
			if(tipoRepositorios.equalsIgnoreCase("ARQUIVO")) {
				repositorioFuncionario = new RepositorioFuncionarioArquivo();
				repositorioPublicacao = new RepositorioPublicacaoArquivo();
				repositorioUsuario = new RepositorioUsuarioArquivo();
			} else if(tipoRepositorios.equalsIgnoreCase("ARRAY")) {
				repositorioFuncionario = new RepositorioFuncionarioArray();
				repositorioPublicacao = new RepositorioPublicacaoArray();
				repositorioUsuario = new RepositorioUsuarioArray();
			} else if(tipoRepositorios.equalsIgnoreCase("LISTA")) {
				repositorioFuncionario = new RepositorioFuncionarioLista();
				repositorioPublicacao = new RepositorioPublicacaoLista();
				repositorioUsuario = new RepositorioUsuarioLista();
			}
			
			this.cadastroFuncionario = new CadastroFuncionarioImpl(repositorioFuncionario);
			this.cadastroPublicacao = new CadastroPublicacaoImpl(repositorioPublicacao);
			this.cadastroUsuario = new CadastroUsuarioImpl(repositorioUsuario);
			this.geradorRelatorios = new GeradorRelatorios(this.cadastroPublicacao);
		} catch(RepositorioException e) {
			e.printStackTrace();
		}
	}
	
	public static Fachada getInstancia() throws ArquivoConfiguracaoNaoEncontradoException {
		if(Fachada.instancia == null) {
			Fachada.instancia = new Fachada();
		}
		return Fachada.instancia;
	}
	
	public void adicionarFuncionario(Funcionario funcionario) throws RepositorioException, FuncionarioJaCadastradoException {
		this.cadastroFuncionario.adicionar(funcionario);
	}
	
	public void removerFuncionario(Funcionario funcionario) throws RepositorioException, FuncionarioNaoEncontradoException {
		this.cadastroFuncionario.remover(funcionario);
	}
	
	public void atualizarFuncionario(Funcionario funcionario) throws RepositorioException, FuncionarioNaoEncontradoException {
		this.cadastroFuncionario.atualizar(funcionario);
	}
	
	public Funcionario buscarFuncionario(Funcionario funcionario) throws RepositorioException, FuncionarioNaoEncontradoException {
		return this.cadastroFuncionario.buscar(funcionario);
	}
	
	public IteratorFuncionario getIteratorFuncionario() throws RepositorioException {
		return this.cadastroFuncionario.getIterator();
	}
	
	public boolean existeFuncionario(Funcionario funcionario) throws RepositorioException {
		return this.cadastroFuncionario.existe(funcionario);
	}
	
	public boolean login(Funcionario funcionario) throws RepositorioException, FuncionarioNaoEncontradoException, LoginSenhaIncorretosException {
		if(this.existeFuncionario(funcionario)) {
			Funcionario f = this.buscarFuncionario(funcionario);
			
			if(funcionario.getLogin().equals(f.getLogin()) && funcionario.getSenha().equals(f.getSenha())) {
				return true;
			} else {
				throw new LoginSenhaIncorretosException();
			}
		} else {
			throw new FuncionarioNaoEncontradoException();
		}
	}
	
	public void adicionarPublicacao(Publicacao publicacao) throws RepositorioException, PublicacaoJaCadastradaException {
		this.cadastroPublicacao.adicionar(publicacao);
	}
	
	public void removerPublicacao(Publicacao publicacao) throws RepositorioException, PublicacaoNaoEncontradaException {
		this.cadastroPublicacao.remover(publicacao);
	}
	
	public void atualizarPublicacao(Publicacao publicacao) throws RepositorioException, PublicacaoNaoEncontradaException {
		this.cadastroPublicacao.atualizar(publicacao);
	}
	
	public Publicacao buscarPublicacao(Publicacao publicacao) throws RepositorioException, PublicacaoNaoEncontradaException {
		return this.cadastroPublicacao.buscar(publicacao);
	}
	
	public IteratorPublicacao getIteratorPublicacao() throws RepositorioException {
		return this.cadastroPublicacao.getIterator();
	}
	
	public boolean existePublicacao(Publicacao publicacao) throws RepositorioException {
		return this.cadastroPublicacao.existe(publicacao);
	}
	
	public void adicionarUsuario(Usuario usuario) throws RepositorioException, UsuarioJaCadastradoException {
		this.cadastroUsuario.adicionar(usuario);
	}
	
	public void removerUsuario(Usuario usuario) throws RepositorioException, UsuarioNaoEncontradoException {
		this.cadastroUsuario.remover(usuario);
	}
	
	public void atualizarUsuario(Usuario usuario) throws RepositorioException, UsuarioNaoEncontradoException {
		this.cadastroUsuario.atualizar(usuario);
	}
	
	public Usuario buscarUsuario(Usuario usuario) throws RepositorioException, UsuarioNaoEncontradoException {
		return this.cadastroUsuario.buscar(usuario);
	}
	
	public IteratorUsuario getIteratorUsuario() throws RepositorioException {
		return this.cadastroUsuario.getIterator();
	}
	
	public boolean existeUsuario(Usuario usuario) throws RepositorioException {
		return this.cadastroUsuario.existe(usuario);
	}
	
	public void emprestarPublicacao(Publicacao publicacao, Usuario usuario) throws RepositorioException, PublicacaoNaoEncontradaException, UsuarioNaoEncontradoException, PublicacaoJaEmprestadaException {
		if(this.existeUsuario(usuario)) {
			if(this.existePublicacao(publicacao)) {
				Publicacao p = this.buscarPublicacao(publicacao);
				
				if((p.getMatriculaUsuarioEmprestimo() != null) && !p.getMatriculaUsuarioEmprestimo().isEmpty()) {
					throw new PublicacaoJaEmprestadaException();
				} else {
					p.setMatriculaUsuarioEmprestimo(usuario.getMatricula());
					this.atualizarPublicacao(p);
				}
			} else {
				throw new PublicacaoNaoEncontradaException();
			}
		} else {
			throw new UsuarioNaoEncontradoException();
		}
	}
	
	public void devolverPublicacao(Publicacao publicacao) throws RepositorioException, PublicacaoNaoEncontradaException, PublicacaoNaoEmprestadaException {
		if(this.existePublicacao(publicacao)) {
			Publicacao p = this.buscarPublicacao(publicacao);
			
			if((p.getMatriculaUsuarioEmprestimo() != null) && !p.getMatriculaUsuarioEmprestimo().isEmpty()) {
				p.setMatriculaUsuarioEmprestimo(null);
				this.atualizarPublicacao(p);
			} else {
				throw new PublicacaoNaoEmprestadaException();
			}
		} else {
			throw new PublicacaoNaoEncontradaException();
		}
	}
	
	public void gerarRelatorioLivrosEmprestados(String caminho, String campoOrdenacao) {
		this.geradorRelatorios.gerarRelatorioLivrosEmprestados(caminho, campoOrdenacao);
	}
	
	public void gerarRelatorioRevistasEmprestadas(String caminho, String campoOrdenacao) {
		this.geradorRelatorios.gerarRelatorioRevistasEmprestadas(caminho, campoOrdenacao);
	}
	
	public Relatorio[] obterRelatoriosGeradosAnteriormente() {
		return this.geradorRelatorios.obterRelatoriosGeradosAnteriormente();
	}
	
	public void gerarRelatorioAnterior(int index, String caminho) {
		this.geradorRelatorios.gerarRelatorioAnterior(index, caminho);
	}
}
